/**
*	@class Matrix4
*
*	@brief This class implements a 4x4 matrix
*
*	@author William McVicar
*/

#ifndef __MATRIX4_H_INCLUDED__
#define __MATRIX4_H_INCLUDED__

// Library Includes

// Local Includes
#include "Vector3.h"
#include "Vector4.h"

// Types

// Constants

// Prototypes

namespace Protein
{

class Matrix4
{

public:

	Matrix4();
	Matrix4( float _11, float _12, float _13, float _14,
				   float _21, float _22, float _23, float _24,
				   float _31, float _32, float _33, float _34,
				   float _41, float _42, float _43, float _44 = 1.0f );

  Matrix4( float* values );
	Matrix4( const Matrix4& other );
	Matrix4& operator=( const Matrix4& rhs );

	bool operator ==( const Matrix4& rhs );
	bool operator !=( const Matrix4& rhs );

	void makeXRot( float theta );
	void makeYRot( float theta );
	void makeZRot( float theta );
  void makeTranslation( const Vector3f& translation );
  void makeScale( const Vector3f& scale );
  void makeAxisAngle( const Vector3f& inAxis, float angle );
  void makeTranspose();
  void makeCameraLookAt( const Vector3f& _krLoc, const Vector3f& _krLookAt, const Vector3f& _krInUp = Vector3f(0,1,0) );
	void makeCameraOrthographicProjection( float _kfWidth, float _kfHeight, float _krNear, float _kfFar );
	void makeCameraPerspectiveProjection( float _kfDMin, float _kfDMax, float _kfRMax, float _kfRMin, float _kfUMax, float _kfUMin );
  void makeIdentity();

  Matrix4 transpose();
  void rotate( float _fYaw, float _fPitch, float _fRoll );
	void translate( const Vector3f& _krAmt );
	void scale( const Vector3f& scale );

  static Matrix4 XRot( float theta );
  static Matrix4 YRot( float theta );
	static Matrix4 ZRot( float theta );
  static Matrix4 AxisAngle( const Vector3f& _krInAxis, float angle );
  static Matrix4 Translation( const Vector3f& _krP );
  static Matrix4 CreateScale( const Vector3f& scale );
	static Matrix4 CreateUniformScale( const float _krValue );

  static Matrix4 CameraLookAt( const Vector3f& _krLoc, const Vector3f& _krLookAt, const Vector3f& _krInUp = Vector3f::j );
  static Matrix4 CameraOrthographicProjection( float _kfWidth, float _kfHeight, float _krNear, float _kfFar );
	static Matrix4 CameraPerspectiveProjection( float _fNear, float _fFar, float right, float left, float top, float bottom );
  static Matrix4 Inverse( const Matrix4& _krIn );
  static void    Transpose( Matrix4& _krIn );

	void ToInverse( const Matrix4& _krIn );
  void Invert();

	Vector3f Transform( Vector3f const& _krIn );
	Vector4f Transform( Vector4f const& _krIn );

	const Vector3f GetTranslation() const;
	Vector3f GetAxis( int _iAxis ) const;

public:

#pragma warning(disable : 4201) 

  union
  {
    struct
    {
      float _11; 
      float _12;
      float _13; 
      float _14;
      float _21; 
      float _22; 
      float _23; 
      float _24;
      float _31; 
      float _32; 
      float _33; 
      float _34;
      float _41; 
      float _42; 
      float _43; 
      float _44;
    };

	  float m_data[16];
    float m_data2D[4][4];
  };

	static const Matrix4 Identity; 
  static const Matrix4 Zero;
};




/**
*	Multiplication Operator for the Matrix4 Class
*
*	@param _krA: A Matrix for the multiplication
*	@param _krB: The other matrix for the multiplication
*	@return: The result of _krA * _krB
*	@author: William McVicar
*/
Matrix4 operator*(Matrix4 const& _krA, Matrix4 const& _krB);

/**
*	This function multiplies two matricies together
*
*	@param _krA: A Matrix for the multiplication
*	@param _krB: The other matrix for the multiplication
*	@param _pResult: a pointer to the matrix which will store the result
*	@author: William McVicar
*/
void MatMult( const Matrix4& _krA, const Matrix4& _krB, Matrix4* _pResult );

/**
*	Multiplication operator to transform a vector by a matrix
*
*	@param _krA The specified matrix to transform a vector by
*	@param _krB A vector3 to be transformed by the specified matrix
*
*	@author William McVicar
*/
const Vector3f operator*( const Matrix4& _krA, const Vector3f& _krB);

/**
*	Multiplication operator to transform a vector by a matrix
*
*	@param _krB The specified matrix to transform a vector by
*	@param _krA A vector3 to be transformed by the specified matrix
*
*	@author William McVicar
*/
const Vector3f operator*( const Vector3f& _krA, const Matrix4& _krB);

/**
*	Multiplication operator to transform a vector by a matrix
*
*	@param _krA The specified matrix to transform a vector by
*	@param _krB A vector4 to be transformed by the specified matrix
*
*	@author William McVicar
*/
const Vector4f operator*( const Matrix4& _krA, const Vector4f& _krB);

/**
*	Multiplication operator to transform a vector by a matrix
*
*	@param _krB The specified matrix to transform a vector by
*	@param _krA A vector4 to be transformed by the specified matrix
*
*	@author William McVicar
*/
const Vector4f operator*( const Vector4f& _krA, const Matrix4& _krB);

Matrix4 operator/( Matrix4 const& _krA, const float _kfB );
Matrix4 operator/( const float _kfB, Matrix4 const& _krA );

}

#pragma warning(default : 4201) 

#endif
